/*================================================================
*   Copyright (C) 2022 Sangfor Ltd. All rights reserved.
*   
*   文件名称：oj_compile.hpp
*   创 建 者：wudu
*   创建日期：2022年04月01日
*   描    述：编译运行用户提交的源代码
*        
*
================================================================*/
#pragma once
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <iostream>
#include <atomic>

#include <json/json.h>

#include "tools.hpp"

class Compiler{
    public:
        /*
         * req : 调用传递的代码信息和标准输入信息
         * resp ： 返回给调用者编译运行的结果, 输出型参数
         * */
        static void CompileAndRun(Json::Value& req, Json::Value& resp){
            //1.检查代码字符串是否存在
            if(req["code"].empty()){
                resp["errorno"] = -1;
                resp["reason"] = "code is empty";
                return;
            }

            //2.保存到文件当中
            string file_header = WriteCodeToFile(req["code"].asString());
            if(file_header.empty()){
                resp["errorno"] = -2;
                resp["reason"] = "Internal error";
                return;
            }
            //3.编译
            if(Complie(file_header) == false){
                resp["errorno"] = -3;
                string reason;
                FileUtil::ReadFile(CompileErrFileName(file_header), &reason);
                resp["reason"] =  reason;
                return;
            } 
            //4.运行
            if(Run(file_header) == false){
                resp["errorno"] = -4;
                string reason;
                FileUtil::ReadFile(StderrFileName(file_header), &reason);
                resp["reason"] =  reason;
                return;
            }
            
            //5.构造响应
            resp["errorno"] = 0;
            resp["reason"] = "Compile and Run ok";

            string str_out;
            FileUtil::ReadFile(StdoutFileName(file_header), &str_out);
            resp["stdout"] = str_out;

            //6.删除临时文件
            DeleteTmpFile(file_header);
        }

    private:

        static void DeleteTmpFile(const string& fileheader){
            unlink(SrcFileName(fileheader).c_str());
            unlink(ExeFileName(fileheader).c_str());
            unlink(CompileErrFileName(fileheader).c_str());
            unlink(StdoutFileName(fileheader).c_str());
            unlink(StderrFileName(fileheader).c_str());
        }

        static bool Run(const string& fileheader){
            pid_t pid = fork();
            if(pid < 0){
                perror("fork");
                return false;
            }else if(pid == 0){
                //child
                int stdout_fd = open(StdoutFileName(fileheader).c_str(), O_CREAT | O_WRONLY, 0666);
                if(stdout_fd < 0){
                    exit(1);
                }
                
                int stderr_fd = open(StderrFileName(fileheader).c_str(), O_CREAT | O_WRONLY, 0666);
                if(stderr_fd < 0){
                    exit(1);
                }

                dup2(stdout_fd, 1);
                dup2(stderr_fd, 2);

                execlp(ExeFileName(fileheader).c_str(), ExeFileName(fileheader).c_str(), NULL);  

                close(stdout_fd);
                close(stderr_fd);
                exit(1);
            }else{
                //fater
                int status;
                wait(&status);

                if(status & 0x7f){
                    return false;
                }
            }
            return true;
        }


        static bool Complie(const string& fileheader){
            //1.创建子进程
            //2.让父进程进行等待（防止子进程变成僵尸进程）
            //3.子进程进行程序替换
            //  替换成为g++程序进行编译
            //      编译成功 ： 一定产生了可执行性程序
            //      编译失败 ： 编译失败的报错信息需要获取
            pid_t pid = fork();
            if(pid < 0){
                perror("fork");
                return false;
            }else if(pid == 0){
                //child
                int fd = open(CompileErrFileName(fileheader).c_str(), O_CREAT | O_WRONLY, 0666);
                if(fd < 0){
                    perror("open");
                    return false;
                }

                /*
                 * 0 : 标准输入 ， stdin
                 * 1 ：标准输出,   stdout
                 * 2 ：标准错误,   strerr
                 * */
                dup2(fd, 2);

                execlp("g++", "g++", SrcFileName(fileheader).c_str(), "-D", "CompileOnline", "-o", ExeFileName(fileheader).c_str(), NULL);

                //如果替换成功了，一定不会走下面的逻辑
                //如果替换失败了， 才会走下面的逻辑
                close(fd);
                exit(1);
            }else{
                //father
                wait(NULL);
            }

            //父进程判断是否生成了可执行程序
            struct stat st;
            if(stat(ExeFileName(fileheader).c_str(), &st) < 0){
                return false;
            }
            return true;
        }

        static string StdoutFileName(const string& fileheader){
            return "./tmp_file/" + fileheader + ".stdout";
        }

        static string StderrFileName(const string& fileheader){
            return "./tmp_file/" + fileheader + ".stderr";
        }

        static string CompileErrFileName(const string& fileheader){
            return "./tmp_file/" + fileheader + ".compileErr";
        }


        static string ExeFileName(const string& fileheader){
            return "./tmp_file/" + fileheader + ".compile";
        }

        static string SrcFileName(const string& fileheader){
            return "./tmp_file/" + fileheader + ".cpp";
        }

        static string WriteCodeToFile(const string& code){
            static atomic_uint id(0);
            string file_header = "tmp_" + to_string(TimeUtil::GetTimeStmapMs()) + "." + to_string(id); 
            id++;

            //tmp_1648779867.0
            FileUtil::WriteFile(SrcFileName(file_header), code);
            return file_header;
        }
};

